package com.rmv.ums.request;

import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dom4j.Element;

import com.rmv.ums.condition.Condition;
import com.rmv.ums.configuration.Configuration;
import com.rmv.ums.configuration.model.ConfigurableNodeException;
import com.rmv.ums.configuration.model.ConfigurableNodeFactory;
import com.rmv.ums.response.ConfigurationResponse;
import com.rmv.ums.response.Response;

public abstract class AbstractRequest implements Request {

	private static final Logger logger = Logger.getLogger(ConfigurationResponse.class);

	protected String id;
	protected Set match = new HashSet();
	protected Set check = new HashSet();
	protected Response response;
	protected Response badCheckResponse;

	public Set getMatch() {
		return match;
	}

	public void setMatch(Set matchs) {
		this.match = matchs;
	}

	public void addMatch(Condition condition) {
		this.match.add(condition);
	}

	public Set getCheck() {
		return check;
	}

	public void setCheck(Set checks) {
		this.check = checks;
	}

	public void addCheck(Condition condition) {
		this.check.add(condition);
	}

	public Response getResponse() {
		return response;
	}

	public void setResponse(Response response) {
		this.response = response;
	}

	public Response getBadCheckResponse() {
		return badCheckResponse;
	}

	public void setBadCheckResponse(Response badCheckResponse) {
		this.badCheckResponse = badCheckResponse;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public abstract boolean process(HttpServletRequest req, HttpServletResponse resp) throws IOException;

	public boolean configure(Element element) {

		if (logger.isDebugEnabled())
			logger.debug("configuring request from:\n" + element.asXML());

		// id
		this.id = element.attributeValue("id");
		if (StringUtils.isBlank(this.id))
			this.id = "REQ" + RandomStringUtils.randomAlphanumeric(8);

		// match
		List conditionMatchNodes = element.selectNodes("match/condition");
		if (conditionMatchNodes.isEmpty())
			return false;
		for (Iterator iter = conditionMatchNodes.iterator(); iter.hasNext();) {
			Element conditionElement = (Element) iter.next();

			try {
				Condition condition = (Condition) ConfigurableNodeFactory.get(Configuration.CONDITION, conditionElement);
				addMatch(condition);
			} catch (ConfigurableNodeException e) {
				logger.warn("match condition error - skipping all request. request:" + element.asXML(), e);
				return false;
			}

		}

		// check
		List conditionCheckNodes = element.selectNodes("check/condition");
		for (Iterator iter = conditionCheckNodes.iterator(); iter.hasNext();) {
			Element conditionElement = (Element) iter.next();

			try {
				Condition condition = (Condition) ConfigurableNodeFactory.get(Configuration.CONDITION, conditionElement);
				addCheck(condition);
			} catch (ConfigurableNodeException e) {
				logger.warn("check condition error - skipping all request. request:" + element.asXML(), e);
				return false;
			}

		}

		// bad check response
		Element badCheckResponseElement = (Element) element.selectSingleNode("check/response");
		if (badCheckResponseElement != null) {
			try {
				Response response = (Response) ConfigurableNodeFactory.get(Configuration.RESPONSE, badCheckResponseElement);
				setBadCheckResponse(response);
			} catch (ConfigurableNodeException e) {
				logger.warn("bad check response error - skipping all request. request:" + element.asXML(), e);
				return false;
			}
		}

		// response
		Element responseElement = (Element) element.selectSingleNode("response");
		if (responseElement == null)
			return false;
		try {
			Response response = (Response) ConfigurableNodeFactory.get(Configuration.RESPONSE, responseElement);
			setResponse(response);
		} catch (ConfigurableNodeException e) {
			logger.warn("response error - skipping all request. request:" + element.asXML(), e);
			return false;
		}

		if (logger.isDebugEnabled())
			logger.debug("configured request:" + this.getId());

		return true;

	}

	public boolean postConfigure(Map responses) {
		boolean ok = true;

		// checking response
		if (response.isReference()) {
			String idResponse = response.getId();
			Response responseReferenced = (Response) responses.get(idResponse);
			if (responseReferenced == null) {
				logger.warn("request reference a not-found response - skipping it\n   request:" + this);
				ok = false;
			} else {
				setResponse(responseReferenced);
			}
		}

		if (!ok)
			return ok;

		// checking response
		Response badCheckResponse = getBadCheckResponse();
		if (badCheckResponse == null) {
			setBadCheckResponse(Configuration.getInstance().getBadCheckDefaultResponse());
		} else {
			if (badCheckResponse.isReference()) {
				String idBadCheckResponse = badCheckResponse.getId();
				Response responseReferenced = (Response) responses.get(idBadCheckResponse);
				if (responseReferenced == null) {
					logger.warn("request reference a not-found bad check response - skipping it\n   request:" + this);
					ok = false;
				} else {
					setBadCheckResponse(responseReferenced);
				}
			}
		}
		return ok;
	}

	public String toString() {
		String str = "\n[request '" + this.id + "':";
		str += "\n   match=";
		for (Iterator iterator = match.iterator(); iterator.hasNext();) {
			Condition condition = (Condition) iterator.next();
			str += "\n      " + condition;
		}
		if (check.size() > 0) {
			str += "\n   check=";
			for (Iterator iterator = check.iterator(); iterator.hasNext();) {
				Condition condition = (Condition) iterator.next();
				str += "\n      " + condition;
			}
			str += "\n      response=" + badCheckResponse;
		}
		str += "\n   response=" + response;
		str += "\n]";

		return str;
	}

}
